//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "LigandEpitopeCompare.h"
#include "Ligand.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>

#include <iostream>
#include <fstream>
#include <zlib.h>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LINGOLENGTH  4
#define LENGTHTEXT   1000
#define LENGTHLINE   100000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
namespace LigandEpitopeSpace {
  
  bool EqualLigand (TNode <SMIData>* residue, void* ref) {

    return (!strcmp(residue->Info()->ID()->Out(), ((String*)ref)->Out()))?true:false;

  }
  
};
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
LigandEpitopeCompare::LigandEpitopeCompare () {
  
  this->filenameLigandEpitope = new String ();
  this->filenameLigandSMI     = new String ();
  this->filenameOut           = new String ();
  this->listLigandEpitopeData = new TListE <LigandEpitopeData> ();
  this->listLigands           = new TListE <SMIData> ();
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
LigandEpitopeCompare::LigandEpitopeCompare (const LigandEpitopeCompare& ligandEpitopeCompare) {
  
  this->filenameLigandEpitope = new String (ligandEpitopeCompare.filenameLigandEpitope);
  this->filenameLigandSMI     = new String (ligandEpitopeCompare.filenameLigandSMI);
  this->filenameOut           = new String (ligandEpitopeCompare.filenameOut);
  this->listLigandEpitopeData = new TListE <LigandEpitopeData> (ligandEpitopeCompare.listLigandEpitopeData);
  this->listLigands           = new TListE <SMIData> (ligandEpitopeCompare.listLigands);
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
LigandEpitopeCompare::LigandEpitopeCompare (const LigandEpitopeCompare* ligandEpitopeCompare) {
  
  this->filenameLigandEpitope = new String (ligandEpitopeCompare->filenameLigandEpitope);
  this->filenameLigandSMI     = new String (ligandEpitopeCompare->filenameLigandSMI);
  this->filenameOut           = new String (ligandEpitopeCompare->filenameOut);
  this->listLigandEpitopeData = new TListE <LigandEpitopeData> (ligandEpitopeCompare->listLigandEpitopeData);
  this->listLigands           = new TListE <SMIData> (ligandEpitopeCompare->listLigands);
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
LigandEpitopeCompare::~LigandEpitopeCompare () {

  if (this->filenameLigandEpitope) delete this->filenameLigandEpitope;
  if (this->filenameLigandSMI)     delete this->filenameLigandSMI;
  if (this->filenameOut)           delete this->filenameOut;
  if (this->listLigandEpitopeData) delete this->listLigandEpitopeData;
  if (this->listLigands)           delete this->listLigands;
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void LigandEpitopeCompare::FilenameLigandEpitope (String* filenameLigandEpitope) {
  
  if (filenameLigandEpitope) this->filenameLigandEpitope->In(filenameLigandEpitope);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void LigandEpitopeCompare::FilenameLigandSMI (String* filenameLigandSMI) {
  
  if (filenameLigandSMI) this->filenameLigandSMI->In(filenameLigandSMI);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void LigandEpitopeCompare::FilenameOut (String* filenameOut) {
  
  if (filenameOut) this->filenameOut->In(filenameOut);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void LigandEpitopeCompare::ListLigandEpitopeData (TListE <LigandEpitopeData>* listLigandEpitopeData) {
  
  if (listLigandEpitopeData) *(this->listLigandEpitopeData) = *(listLigandEpitopeData);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void LigandEpitopeCompare::ListLigands (TListE <SMIData>* listLigands) {
  
  if (listLigands) *(this->listLigands) = *(listLigands);
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* LigandEpitopeCompare::FilenameLigandEpitope (void) {
  
  return this->filenameLigandEpitope;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* LigandEpitopeCompare::FilenameLigandSMI (void) {
  
  return this->filenameLigandSMI;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* LigandEpitopeCompare::FilenameOut (void) {
  
  return this->filenameOut;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <LigandEpitopeData>* LigandEpitopeCompare::ListLigandEpitopeData (void) {
  
  return this->listLigandEpitopeData;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <SMIData>* LigandEpitopeCompare::ListLigands (void) {
  
  return this->listLigands;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool LigandEpitopeCompare::LoadData (void) {
  
  if (!this->filenameLigandEpitope || !this->filenameLigandEpitope->Length() ||
      !this->filenameLigandSMI     || !this->filenameLigandSMI               ||
      !this->listLigandEpitopeData || !this->listLigands) 
    return false;

  this->listLigandEpitopeData->Clear();
  this->listLigands->Clear();

  bool isGZFile           = (this->filenameLigandEpitope->Contain("gz"))?1:0;
  bool isGZFileLigandList = (this->filenameLigandSMI->Contain("gz"))?1:0;

  fstream fileIn,    fileLigandList;
  gzFile  zipFileIn, zipFileLigandList;
  
  char    buffer [LENGTHLINE + 1];
  String* data = new String ();

  if (isGZFile) {
    zipFileIn = gzopen (this->filenameLigandEpitope->Out(), "rb9");
    if (!zipFileIn) return false;
  }
  else {
    fileIn.open (this->filenameLigandEpitope->Out(), fstream::in);
    if (!fileIn.is_open()) return false;
  }

  while (!((isGZFile)?gzeof(zipFileIn):fileIn.eof())) {
    
    if (isGZFile) gzgets(zipFileIn, buffer, LENGTHLINE);
    else          fileIn.getline(buffer, LENGTHLINE);
    
    if (!strlen(buffer)) continue;
    
    LigandEpitopeData* newLigandEpitopeData = new LigandEpitopeData ();
    
    if (newLigandEpitopeData->LoadDataWithoutLingoSim(buffer)) 
      this->listLigandEpitopeData->Add(newLigandEpitopeData);
    else          
      delete newLigandEpitopeData;
    
  }

  if (isGZFile) gzclose(zipFileIn);
  else          fileIn.close();

  if (data) delete data;

  if (isGZFileLigandList) {
    zipFileLigandList = gzopen (this->filenameLigandSMI->Out(), "rb9");
    if (!zipFileLigandList) return false;
  }
  else {
    fileLigandList.open (this->filenameLigandSMI->Out(), fstream::in);
    if (!fileLigandList.is_open()) return false;
  }

  while (!((isGZFileLigandList)?gzeof(zipFileLigandList):fileLigandList.eof())) {
    
    if (isGZFileLigandList) gzgets(zipFileLigandList, buffer, LENGTHLINE);
    else                    fileLigandList.getline(buffer, LENGTHLINE);
    
    if (!strlen(buffer)) continue;
    
    SMIData* newSMIData = new SMIData ();
    
    if (newSMIData->LoadData(buffer)) this->listLigands->Add(newSMIData);
    else                              delete newSMIData;
    
  }

  if (isGZFileLigandList) gzclose(zipFileLigandList);
  else                    fileLigandList.close();

  return true;  
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool LigandEpitopeCompare::LoadDataSMI (void) {
  
  if (!this->filenameLigandSMI     || !this->filenameLigandSMI               ||
      !this->listLigandEpitopeData || !this->listLigands) 
    return false;

  this->listLigands->Clear();

  bool isGZFileLigandList = (this->filenameLigandSMI->Contain("gz"))?1:0;

  fstream fileLigandList;
  gzFile  zipFileLigandList;
  
  char    buffer [LENGTHLINE + 1];
  
  if (isGZFileLigandList) {
    zipFileLigandList = gzopen (this->filenameLigandSMI->Out(), "rb9");
    if (!zipFileLigandList) return false;
  }
  else {
    fileLigandList.open (this->filenameLigandSMI->Out(), fstream::in);
    if (!fileLigandList.is_open()) return false;
  }

  while (!((isGZFileLigandList)?gzeof(zipFileLigandList):fileLigandList.eof())) {
    
    if (isGZFileLigandList) gzgets(zipFileLigandList, buffer, LENGTHLINE);
    else                    fileLigandList.getline(buffer, LENGTHLINE);
    
    if (!strlen(buffer)) continue;
    
    SMIData* newSMIData = new SMIData ();
    
    if (newSMIData->LoadData(buffer)) this->listLigands->Add(newSMIData);
    else                              delete newSMIData;
    
  }

  if (isGZFileLigandList) gzclose(zipFileLigandList);
  else                    fileLigandList.close();

  return true;  
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool LigandEpitopeCompare::ComputeLingoSim (void) {
  
  if (!this->listLigandEpitopeData || !this->listLigandEpitopeData->Length() ||
      !this->listLigands           || !this->listLigands->Length())
    return false;
  
  this->listLigandEpitopeData->SetInitial();
  while (this->listLigandEpitopeData->SetNext()) {
    
    LigandEpitopeData* currentLigandEpitopeData = this->listLigandEpitopeData->GetCurrent();
    
    SMIData* searchSMIData1 = this->listLigands->FirstOcurrence(LigandEpitopeSpace::EqualLigand, currentLigandEpitopeData->LigandName1());
    SMIData* searchSMIData2 = this->listLigands->FirstOcurrence(LigandEpitopeSpace::EqualLigand, currentLigandEpitopeData->LigandName2());
    
    if (!searchSMIData1 || !searchSMIData2) {
      if (!searchSMIData1) cout << "Error searching the Ligands > " << currentLigandEpitopeData->LigandName1()->Out() << endl;
      if (!searchSMIData2) cout << "Error searching the Ligands > " << currentLigandEpitopeData->LigandName2()->Out() << endl;
      continue;
    }
    
    currentLigandEpitopeData->LingoSimValue(this->ObtainLingoSim(searchSMIData1->Ligand()->Out(), searchSMIData2->Ligand()->Out()));
    
  }
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool LigandEpitopeCompare::ComputeLingoSimRandom (void) {
  
  if (!this->filenameOut || !this->filenameOut->Length())
    return false;
  
  bool isGZFile = (this->filenameOut->Contain("gz"))?1:0;

  fstream fileOut;
  gzFile  zipFileOut;
  
  if (isGZFile) {
    zipFileOut = gzopen (this->filenameOut->Out(), "rb9");
    if (!zipFileOut) return false;
  }
  else {
    fileOut.open (this->filenameOut->Out(), fstream::out);
    if (!fileOut.is_open()) return false;
  }
  

  this->listLigandEpitopeData->SetInitial();
  while (this->listLigandEpitopeData->SetNext()) {
    
    LigandEpitopeData* currentLigandEpitopeData = this->listLigandEpitopeData->GetCurrent();
    
    SMIData* searchSMIData1 = this->listLigands->FirstOcurrence(LigandEpitopeSpace::EqualLigand, currentLigandEpitopeData->LigandName1());
    SMIData* searchSMIData2 = this->listLigands->FirstOcurrence(LigandEpitopeSpace::EqualLigand, currentLigandEpitopeData->LigandName2());
    
    if (!searchSMIData1 || !searchSMIData2) {
      if (!searchSMIData1) cout << "Error searching the Ligands > " << currentLigandEpitopeData->LigandName1()->Out() << endl;
      if (!searchSMIData2) cout << "Error searching the Ligands > " << currentLigandEpitopeData->LigandName2()->Out() << endl;
      continue;
    }
    
    currentLigandEpitopeData->LingoSimValue(this->ObtainLingoSim(searchSMIData1->Ligand()->Out(), searchSMIData2->Ligand()->Out()));
    
  }
  
  
  
  
  this->listLigandEpitopeData->SetInitial();
  while (this->listLigandEpitopeData->SetNext()) 
    if (isGZFile) gzprintf(zipFileOut, "%s\n", this->listLigandEpitopeData->GetCurrent()->ToString()->Out());
    else          fileOut << this->listLigandEpitopeData->GetCurrent()->ToString()->Out() << endl;
  
  if (isGZFile) gzclose(zipFileOut);
  else          fileOut.close();
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double LigandEpitopeCompare::ObtainLingoSim (char* lingoString1, char* lingoString2) {

  if (strlen(lingoString1) < LINGOLENGTH || strlen(lingoString2) < LINGOLENGTH) return 0;

  int  i, j, k, z;
  int  length1 = strlen(lingoString1), length2 = strlen(lingoString2);
  
  char tmpLingo1[length1], tmpLingo2[length2];

  //Change the Cl bu L and Br by R in the two lingo sequences
  for (i = 0, j = 0, k = 0; i < ((length1 > length2)?length1:length2); i++) {
    
    if (i < length1) {
      if      ((lingoString1[i] == 'l') || (lingoString1[i] == 'r')) tmpLingo1[j - 1] = (lingoString1[i] == 'l')?'L':'R';
      //else if (isdigit(lingoString1[i]))                             tmpLingo1[j++]   = '0';
      else                                                           tmpLingo1[j++]   = lingoString1[i];
    }
    
    if (i < length2) {
      if      ((lingoString2[i] == 'l') || (lingoString2[i] == 'r')) tmpLingo2[k - 1] = (lingoString2[i] == 'l')?'L':'R';
      //else if (isdigit(lingoString2[i]))                             tmpLingo2[k++]   = '0';
      else                                                           tmpLingo2[k++]   = lingoString2[i];
    }
    
  }
  
  tmpLingo1[j] = tmpLingo2[k] = '\0';

  /*j -= (LINGOLENGTH - 1); k -= (LINGOLENGTH - 1);*/
  j = length1 - (LINGOLENGTH - 1); k = length2 - (LINGOLENGTH - 1);
  
  int    L = j + k; //Total Lingos in two sequences
  double similarityValue = 0, Nai, Nbi;

  for (i = 0; i < j + k; i++) {

    char   compare[5];
    bool   analize = true;

    Nai = 0, Nbi = 0;

    if (i < j) {

      strncpy(compare, tmpLingo1 + i, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          if (z < i) {
            analize = false;
            break;
          }
          Nai++;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          Nbi++;
        }
      }
    }
    else {

      strncpy(compare, tmpLingo2 + i - j, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          analize = false;
          break;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          if (z < i - j) {
            analize = false;
            break;
          }
          Nbi++;
        }
      }
    }

    if (analize == true) similarityValue += (((Nai - Nbi) >= 0)?(1.0 - ((double)(Nai - Nbi)/(double)(Nai + Nbi))):(1 - ((double)(Nbi - Nai)/(double)(Nai + Nbi))));
    else                 L--;
  }

  return (double)((double)similarityValue/(double)L);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool LigandEpitopeCompare::WriteResults (void) {
  
  if (!this->filenameOut || !this->filenameOut->Length())
    return false;
  
  bool isGZFile = (this->filenameOut->Contain("gz"))?1:0;

  fstream fileOut;
  gzFile  zipFileOut;
  
  if (isGZFile) {
    zipFileOut = gzopen (this->filenameOut->Out(), "rb9");
    if (!zipFileOut) return false;
  }
  else {
    fileOut.open (this->filenameOut->Out(), fstream::out);
    if (!fileOut.is_open()) return false;
  }
  
  this->listLigandEpitopeData->SetInitial();
  while (this->listLigandEpitopeData->SetNext()) 
    if (isGZFile) gzprintf(zipFileOut, "%s\n", this->listLigandEpitopeData->GetCurrent()->ToString()->Out());
    else          fileOut << this->listLigandEpitopeData->GetCurrent()->ToString()->Out() << endl;
  
  if (isGZFile) gzclose(zipFileOut);
  else          fileOut.close();
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
LigandEpitopeCompare LigandEpitopeCompare::operator= (const LigandEpitopeCompare& ligandEpitopeCompare) {
  
  this->filenameLigandEpitope->In(ligandEpitopeCompare.filenameLigandEpitope);
  this->filenameLigandSMI->In(ligandEpitopeCompare.filenameLigandSMI);
  this->filenameOut->In(ligandEpitopeCompare.filenameOut);
  
  *(this->listLigandEpitopeData) = *(ligandEpitopeCompare.listLigandEpitopeData);
  *(this->listLigands)           = *(ligandEpitopeCompare.listLigands);
  
  return *this;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//

